Skip to content

PENG-7708-python-sdk-zoneexport#145

Merged
fformica merged 18 commits into
masterfrom
PENG-7708-python-sdk-zoneexport
Jul 15, 2026
Merged

PENG-7708-python-sdk-zoneexport#145
fformica merged 18 commits into
masterfrom
PENG-7708-python-sdk-zoneexport

Conversation

@soniafrancisNS1

Copy link
Copy Markdown
Contributor
  • Add export() method to REST API and Zone class
  • Add unit test for zone export
  • Add simple usage example

Allows customers to export zones in BIND format for backup/migration.

- Add export() method to REST API and Zone class
- Add unit test for zone export
- Add simple usage example

Allows customers to export zones in BIND format for backup/migration.
@ddevine-NS1

Copy link
Copy Markdown

Comment thread examples/zone-export.py Outdated
print(zone_file)

# save to a file
with open("example.com.zone", "w") as f:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
with open("example.com.zone", "w") as f:
with open("example.com.txt", "w") as f:

Usually zone files just have a .txt extension; and zone is a tld, so calling them .zone could be a bit confusing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to example.com.txt in line 31.

Comment thread examples/zone-export.py
Comment thread ns1/rest/zones.py Outdated
errback=errback,
)

def export(self, zone, callback=None, errback=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def export(self, zone, callback=None, errback=None):
def get_zonefile_export(self, zone, callback=None, errback=None):

@soniafrancisNS1 soniafrancisNS1 Jan 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to get_zonefile_export()

Comment thread ns1/rest/zones.py Outdated
errback=errback,
)

def initiate_export(self, zone, callback=None, errback=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def initiate_export(self, zone, callback=None, errback=None):
def initiate_zonefile_export(self, zone, callback=None, errback=None):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to initiate_zonefile_export()

Comment thread ns1/rest/zones.py Outdated
errback=errback,
)

def export_status(self, zone, callback=None, errback=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def export_status(self, zone, callback=None, errback=None):
def status_zonefile_export(self, zone, callback=None, errback=None):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to status_zonefile_export()

Comment thread ns1/rest/zones.py Outdated

def export(self, zone, callback=None, errback=None):
"""
Export zone as BIND-compatible zone file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could probably say this downloads the zonefile.

Comment thread ns1/zones.py Outdated
zone=self.zone, callback=callback, errback=errback, **kwargs
)

def export(self, callback=None, errback=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replace these with a single function that:

  1. Initialises the export PUT
  2. Polls the status, until "COMPLETE" or "FAILED" or some timeout hits.
  3. Calls/returns the download endpoint.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhanced the example with detailed comments explaining the complete workflow. The export() method now handles all three steps automatically: initiate, poll status, and download.

Comment thread ns1/zones.py Outdated
:return: zone file content as string
:raises ZoneException: if export fails or times out
"""
import time

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't import packages inside functions, can you move this to the top with the other imports?

Comment thread ns1/zones.py Outdated
self.zone, callback=callback, errback=errback
)

if callback:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary here? you're already sending get_zonefile_export so any callback will get called above?

Comment thread ns1/zones.py Outdated
import time

# Initiate the export
self._rest.initiate_zonefile_export(self.zone)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check the response here; make sure it's 200.

If something goes wrong here then we can't tell and it'll start polling for something that will never finish.

Comment thread ns1/zones.py Outdated
status_response = self._rest.status_zonefile_export(self.zone)
status = status_response.get("status")

if status == "COMPLETE":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if status == "COMPLETE":
if status == "COMPLETED":

Comment thread examples/zone-export.py Outdated
Comment thread examples/zone-export.py Outdated
Comment thread ns1/rest/zones.py Outdated
@soniafrancisNS1
soniafrancisNS1 marked this pull request as draft January 8, 2026 15:36
Comment thread ns1/rest/zones.py Outdated
# Note: This endpoint returns raw zone file text, not JSON
# The transport layer will try to parse it as JSON and fail
# We catch that exception and extract the raw body text
from ns1.rest.errors import ResourceException

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports need to go on the top of the file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved from .errors import ResourceException at the top

Comment thread ns1/rest/zones.py Outdated
# The body is the third argument in ResourceException
if hasattr(e, 'args') and len(e.args) >= 3:
body = e.args[2]
if callback:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the callback being checked here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the callback check was unnecessary since _make_request() already handles callbacks. I've removed it. Now the method simply returns e.body directly when it's a valid zonefile response.

Comment thread ns1/rest/zones.py Outdated
# If it's about invalid JSON, that's expected - extract the body
if "invalid json in response" in str(e):
# The body is the third argument in ResourceException
if hasattr(e, 'args') and len(e.args) >= 3:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResourceException is a custom error, you can access the body like:

body = e.body

invalid json in response is too generic, since any http/txt response will probably result in this error too and this will just assume it's a zonefile.

Can you update this to check that the response code is valid otherwise raise the error.
And to check that the content type header is valid too.

Comment thread examples/zone-export.py Outdated
# 1. Initiate the export job
# 2. Poll the status until complete or failed
# 3. Download and return the zone file content
zone = api.loadZone("example.com")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you set example.com to be a variable and then use that for the script, it's a bit neater for using it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the output filename dynamic using f"{zone_name}.txt"

@hhellyer hhellyer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, I think there's one improvement/simplification you can make and we probably ought to get the copyright statement correct but no major issues!

Comment thread ns1/rest/zones.py Outdated
Comment thread examples/zone-export.py Outdated
Comment thread ns1/zones.py
hhellyer
hhellyer previously approved these changes Jan 12, 2026

@hhellyer hhellyer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - Thanks for making changes.

@soniafrancisNS1
soniafrancisNS1 marked this pull request as ready for review January 26, 2026 10:16
@fformica

Copy link
Copy Markdown
Contributor

@soniafrancisNS1 can you have a look at #150 and check how it integrates with this PR?

lhost added a commit to lhost/ns1-python that referenced this pull request Jul 14, 2026
@lhost

lhost commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

They are 2 ways to export zone:

  • in BIND format
  • in JSON format.

The name of the example examples/zone-export.py maybe should get renamed to examples/zone-export-bind.py. I will rename my example (accidentally with the same name in #150) to examples/zone-export-json.py.

@cmckenna-ibm cmckenna-ibm left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I see my comments have been resolved, thank you @soniafrancisNS1.

Comment thread examples/zone-export.py Outdated
fformica pushed a commit that referenced this pull request Jul 15, 2026
* Allow parameter export=True for zone export

With the parameter export=True the output of zone export looks
differently:

- key `short_answers` becomes 'answers'
- key 'id' is removed
- key 'zone_name' is removed

* Reformat code with black

* Pass params to _make_request() only if it's not None

* Rename example to avoid filename collision with #145
@fformica
fformica merged commit 55ceef5 into master Jul 15, 2026
9 checks passed
@fformica
fformica deleted the PENG-7708-python-sdk-zoneexport branch July 15, 2026 12:46
Comment thread examples/zone-export.py
# Save to a file
output_file = f"{zone_name}.txt"
with open(output_file, "w") as f:
with open("example.com.txt", "w") as f:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output_file variables was created from zone_name. Now the filename is hardcoded on 2 places, variable zone_name is not used when it should be.

@fformica fformica Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, too late 😞 Thanks for catching that; I thought the file had been renamed too. Let's fix it in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants